package com.fary.web.servlet.handler;

import com.fary.beans.factory.BeanFactoryUtils;
import com.fary.beans.factory.BeanNameAware;
import com.fary.core.Ordered;
import com.fary.core.SpringException;
import com.fary.util.AntPathMatcher;
import com.fary.util.Assert;
import com.fary.util.PathMatcher;
import com.fary.web.HttpRequestHandler;
import com.fary.web.context.request.WebRequestInterceptor;
import com.fary.web.context.support.WebApplicationObjectSupport;
import com.fary.web.cors.*;
import com.fary.web.servlet.HandlerExecutionChain;
import com.fary.web.servlet.HandlerInterceptor;
import com.fary.web.servlet.HandlerMapping;
import com.fary.web.util.UrlPathHelper;

import javax.servlet.DispatcherType;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport implements HandlerMapping, Ordered, BeanNameAware {

    private Object defaultHandler;

    // url路径计算的辅助类、工具类
    private UrlPathHelper urlPathHelper = new UrlPathHelper();
    // Ant风格的Path匹配模式~  解决如/books/{id}场景
    private PathMatcher pathMatcher = new AntPathMatcher();
    // 保存着拦截器们~~~
    private final List<Object> interceptors = new ArrayList<>();
    // 从interceptors中解析得到,直接添加给全部handler
    private final List<HandlerInterceptor> adaptedInterceptors = new ArrayList<>();
    // 跨域相关的配置~
    private CorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
    private CorsProcessor corsProcessor = new DefaultCorsProcessor();
    // 最低的顺序（default: same as non-Ordered）
    private int order = Ordered.LOWEST_PRECEDENCE;  // default: same as non-Ordered

    private String beanName;


    /**
     * 默认的处理器
     */
    public void setDefaultHandler(Object defaultHandler) {
        this.defaultHandler = defaultHandler;
    }

    public Object getDefaultHandler() {
        return this.defaultHandler;
    }

    /**
     * /json/uploadFile 请求路径
     * 为true时，匹配规则是取   /json/uploadFile
     * 为false时，匹配规则是取  /uploadFile
     */
    public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
        this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
        if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) {
            ((UrlBasedCorsConfigurationSource) this.corsConfigurationSource).setAlwaysUseFullPath(alwaysUseFullPath);
        }
    }

    /**
     * 上下文路径和请求URI是否应当被解码
     */
    public void setUrlDecode(boolean urlDecode) {
        this.urlPathHelper.setUrlDecode(urlDecode);
        if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) {
            ((UrlBasedCorsConfigurationSource) this.corsConfigurationSource).setUrlDecode(urlDecode);
        }
    }

    /**
     * 请求URI中，是否删除;后的部分
     */
    public void setRemoveSemicolonContent(boolean removeSemicolonContent) {
        this.urlPathHelper.setRemoveSemicolonContent(removeSemicolonContent);
        if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) {
            ((UrlBasedCorsConfigurationSource) this.corsConfigurationSource).setRemoveSemicolonContent(removeSemicolonContent);
        }
    }

    /**
     * 设置 UrlPathHelper 以用于解析查找路径。使用它来使用自定义子类覆盖默认 UrlPathHelper，
     * 或在多个 HandlerMappings 和 MethodNameResolver 之间共享通用 UrlPathHelper 设置。
     */
    public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
        Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
        this.urlPathHelper = urlPathHelper;
        if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) {
            ((UrlBasedCorsConfigurationSource) this.corsConfigurationSource).setUrlPathHelper(urlPathHelper);
        }
    }

    public UrlPathHelper getUrlPathHelper() {
        return this.urlPathHelper;
    }

    /**
     * 设置 PathMatcher 实现以用于将 URL 路径与注册的 URL 模式匹配。默认为 AntPathMatcher。
     */
    public void setPathMatcher(PathMatcher pathMatcher) {
        Assert.notNull(pathMatcher, "PathMatcher must not be null");
        this.pathMatcher = pathMatcher;
        if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) {
            ((UrlBasedCorsConfigurationSource) this.corsConfigurationSource).setPathMatcher(pathMatcher);
        }
    }

    public PathMatcher getPathMatcher() {
        return this.pathMatcher;
    }

    /**
     * 设置拦截器
     */
    public void setInterceptors(Object... interceptors) {
        this.interceptors.addAll(Arrays.asList(interceptors));
    }

    /**
     * 设置一个UrlBasedCorsConfigurationSource  Map表示它的一些属性们~~~
     */
    public void setCorsConfigurations(Map<String, CorsConfiguration> corsConfigurations) {
        Assert.notNull(corsConfigurations, "corsConfigurations must not be null");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.setCorsConfigurations(corsConfigurations);
        source.setPathMatcher(this.pathMatcher);
        source.setUrlPathHelper(this.urlPathHelper);
        this.corsConfigurationSource = source;
    }

    public void setCorsConfigurationSource(CorsConfigurationSource corsConfigurationSource) {
        Assert.notNull(corsConfigurationSource, "corsConfigurationSource must not be null");
        this.corsConfigurationSource = corsConfigurationSource;
    }

    /**
     * Get the "global" CORS configurations.
     *
     * @deprecated as of 5.1 since it is now possible to set a {@link CorsConfigurationSource} which is not a
     * {@link UrlBasedCorsConfigurationSource}. Expected to be removed in 5.2.
     */
    @Deprecated
    public Map<String, CorsConfiguration> getCorsConfigurations() {
        if (this.corsConfigurationSource instanceof UrlBasedCorsConfigurationSource) {
            return ((UrlBasedCorsConfigurationSource) this.corsConfigurationSource).getCorsConfigurations();
        } else {
            throw new IllegalStateException("No CORS configurations available when the source is not an UrlBasedCorsConfigurationSource");
        }
    }

    public void setCorsProcessor(CorsProcessor corsProcessor) {
        Assert.notNull(corsProcessor, "CorsProcessor must not be null");
        this.corsProcessor = corsProcessor;
    }

    /**
     * Return the configured {@link CorsProcessor}.
     */
    public CorsProcessor getCorsProcessor() {
        return this.corsProcessor;
    }

    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    public int getOrder() {
        return this.order;
    }

    @Override
    public void setBeanName(String name) {
        this.beanName = name;
    }

    protected String formatMappingName() {
        return this.beanName != null ? "'" + this.beanName + "'" : "<unknown>";
    }

    /**
     * 初始化拦截器
     */
    @Override
    protected void initApplicationContext() throws SpringException {
        // 给子类扩展：增加拦截器，默认为空实现
        extendInterceptors(this.interceptors);
        // 找到所有MappedInterceptor类型的bean添加到adaptedInterceptors中
        detectMappedInterceptors(this.adaptedInterceptors);
        // 将interceptors中的拦截器取出放入adaptedInterceptors
        // 如果是WebRequestInterceptor类型的拦截器  需要用WebRequestHandlerInterceptorAdapter进行包装适配
        initInterceptors();
    }

    /**
     * 扩展拦截器
     */
    protected void extendInterceptors(List<Object> interceptors) {
    }

    /**
     * Detect beans of type {@link MappedInterceptor} and add them to the list of mapped interceptors.
     * <p>This is called in addition to any {@link MappedInterceptor MappedInterceptors} that may have been provided
     * via {@link #setInterceptors}, by default adding all beans of type {@link MappedInterceptor}
     * from the current context and its ancestors. Subclasses can override and refine this policy.
     *
     * @param mappedInterceptors an empty list to add {@link MappedInterceptor} instances to
     */
    protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
        mappedInterceptors.addAll(
                BeanFactoryUtils.beansOfTypeIncludingAncestors(
                        obtainApplicationContext(), MappedInterceptor.class, true, false).values());
    }

    /**
     * 它就是把调用者放进来的interceptors们，适配成HandlerInterceptor然后统一放在`adaptedInterceptors`里面装着
     */
    protected void initInterceptors() {
        if (!this.interceptors.isEmpty()) {
            for (int i = 0; i < this.interceptors.size(); i++) {
                Object interceptor = this.interceptors.get(i);
                if (interceptor == null) {
                    throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null");
                }
                this.adaptedInterceptors.add(adaptInterceptor(interceptor));
            }
        }
    }

    /**
     * 适配其实也很简单~就是支持源生的HandlerInterceptor以及WebRequestInterceptor两种情况而已
     */
    protected HandlerInterceptor adaptInterceptor(Object interceptor) {
        if (interceptor instanceof HandlerInterceptor) {
            return (HandlerInterceptor) interceptor;
        } else if (interceptor instanceof WebRequestInterceptor) {
            // WebRequestHandlerInterceptorAdapter它就是个`HandlerInterceptor`，内部持有一个WebRequestInterceptor的引用而已
            // 内部使用到了DispatcherServletWebRequest包request和response包装成`WebRequest`等等
            return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
        } else {
            throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
        }
    }

    /**
     * 获取所有适配之后的拦截器
     */
    protected final HandlerInterceptor[] getAdaptedInterceptors() {
        return (!this.adaptedInterceptors.isEmpty() ?
                this.adaptedInterceptors.toArray(new HandlerInterceptor[0]) : null);
    }

    /**
     * 获取所有MappedInterceptor拦截器
     */
    protected final MappedInterceptor[] getMappedInterceptors() {
        List<MappedInterceptor> mappedInterceptors = new ArrayList<>(this.adaptedInterceptors.size());
        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            if (interceptor instanceof MappedInterceptor) {
                mappedInterceptors.add((MappedInterceptor) interceptor);
            }
        }
        return (!mappedInterceptors.isEmpty() ? mappedInterceptors.toArray(new MappedInterceptor[0]) : null);
    }


    /**
     * 查找处理器
     */
    @Override
    public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
        // 获取HandlerMethod
        Object handler = getHandlerInternal(request);
        if (handler == null) {
            handler = getDefaultHandler();
        }
        if (handler == null) {
            return null;
        }
        // Bean name or resolved handler?
        if (handler instanceof String) {
            String handlerName = (String) handler;
            handler = obtainApplicationContext().getBean(handlerName);
        }

        // 获取调用链
        HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);

        if (logger.isTraceEnabled()) {
            logger.trace("Mapped to " + handler);
        } else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
            logger.debug("Mapped to " + executionChain.getHandler());
        }

        // 判断是否需要添加CorsInterceptor
        if (CorsUtils.isCorsRequest(request)) {
            // 全局CORS配置 WebMvcConfigurer.addCorsMappings
            CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
            // 单个处理器CORS配置
            CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
            CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
            executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
        }

        return executionChain;
    }

    protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;

    protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
        HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler));

        String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
        for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
            if (interceptor instanceof MappedInterceptor) {
                MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
                if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
                    chain.addInterceptor(mappedInterceptor.getInterceptor());
                }
            } else {
                chain.addInterceptor(interceptor);
            }
        }
        return chain;
    }

    /**
     * Retrieve the CORS configuration for the given handler.
     *
     * @param handler the handler to check (never {@code null}).
     * @param request the current request.
     * @return the CORS configuration for the handler, or {@code null} if none
     * @since 4.2
     */
    protected CorsConfiguration getCorsConfiguration(Object handler, HttpServletRequest request) {
        Object resolvedHandler = handler;
        if (handler instanceof HandlerExecutionChain) {
            resolvedHandler = ((HandlerExecutionChain) handler).getHandler();
        }
        if (resolvedHandler instanceof CorsConfigurationSource) {
            return ((CorsConfigurationSource) resolvedHandler).getCorsConfiguration(request);
        }
        return null;
    }

    /**
     * Update the HandlerExecutionChain for CORS-related handling.
     * <p>For pre-flight requests, the default implementation replaces the selected
     * handler with a simple HttpRequestHandler that invokes the configured
     * {@link #setCorsProcessor}.
     * <p>For actual requests, the default implementation inserts a
     * HandlerInterceptor that makes CORS-related checks and adds CORS headers.
     *
     * @param request the current request
     * @param chain   the handler chain
     * @param config  the applicable CORS configuration (possibly {@code null})
     * @since 4.2
     */
    protected HandlerExecutionChain getCorsHandlerExecutionChain(HttpServletRequest request,
                                                                 HandlerExecutionChain chain, CorsConfiguration config) {

        if (CorsUtils.isPreFlightRequest(request)) {
            HandlerInterceptor[] interceptors = chain.getInterceptors();
            chain = new HandlerExecutionChain(new PreFlightHandler(config), interceptors);
        } else {
            chain.addInterceptor(new CorsInterceptor(config));
        }
        return chain;
    }


    private class PreFlightHandler implements HttpRequestHandler, CorsConfigurationSource {

        private final CorsConfiguration config;

        public PreFlightHandler(CorsConfiguration config) {
            this.config = config;
        }

        @Override
        public void handleRequest(HttpServletRequest request, HttpServletResponse response) throws IOException {
            corsProcessor.processRequest(this.config, request, response);
        }

        @Override
        public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
            return this.config;
        }
    }


    private class CorsInterceptor extends HandlerInterceptorAdapter implements CorsConfigurationSource {

        private final CorsConfiguration config;

        public CorsInterceptor(CorsConfiguration config) {
            this.config = config;
        }

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            return corsProcessor.processRequest(this.config, request, response);
        }

        @Override
        public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
            return this.config;
        }
    }

}